import { Injectable } from "@angular/core";
import { Observable } from 'rxjs/Observable';
import { Http, Response, Headers, RequestOptions, RequestOptionsArgs, ConnectionBackend, Connection } from '@angular/http';

import { StatusCode } from '../model/status.code';
import { IProvider } from "../provider.interface";
import { RESTEnum } from '../model/parameters';
import { LoggerService, LoggerInfo } from '../../logger/logger.service';

// @Injectable()
export class RESTfulProvider implements IProvider {
    instance: any;
    logger: LoggerService;
    private http: Http;
    constructor(instance: any) {
        this.instance = instance;
        this.logger = new LoggerService();
    }

    set(http: Http) {
        this.http = http;
    }

    // start =====  general functions of RESTFul & Websocket & Mock =====
    query(paras: any, sucCallback: Function, failCallback: Function): void {
        let ob: Promise<any> = this._callService(RESTEnum.GET, this.instance.query.serviceUrl, this.instance.query.httpHeaders);
        ob.then(x => sucCallback(x)).catch(y => failCallback(y));
        //simple callback method, maybe we can provide Observable obj for complicate situation.
        //ob.toPromise().then(sucCallback(this._extractData)).catch(failCallback(this._catchHandler));
    }

    get(paras: any, sucCallback: Function, failCallback: Function): void {
        let ob: Promise<any> = this._callService(RESTEnum.GET, this.instance.get.serviceUrl, this.instance.get.httpHeaders, paras);
        ob.then(x => sucCallback(x)).catch(y => failCallback(y));
        //simple callback method, maybe we can provide Observable obj for complicate situation.
        //ob.toPromise().then(sucCallback("this._extractData")).catch(failCallback(this._catchHandler));
    }

    add(paras: any, sucCallback: Function, failCallback: Function): void {
        let ob: Promise<any> = this._callService(RESTEnum.POST, this.instance.add.serviceUrl, this.instance.add.httpHeaders, undefined, paras);
        ob.then(x => sucCallback(x)).catch(y => failCallback(y));
        //simple callback method, maybe we can provide Observable obj for complicate situation.
        //ob.toPromise().then(sucCallback(this._extractData)).catch(failCallback(this._catchHandler));
    }

    update(paras: any, sucCallback: Function, failCallback: Function): void {
        let ob: Promise<any> = this._callService(RESTEnum.PUT, this.instance.update.serviceUrl, this.instance.update.httpHeaders, undefined, paras);
        ob.then(x => sucCallback(x)).catch(y => failCallback(y));
        //simple callback method, maybe we can provide Observable obj for complicate situation.
        //ob.toPromise().then(sucCallback(this._extractData)).catch(failCallback(this._catchHandler));
    }

    delete(paras: any, sucCallback: Function, failCallback: Function): void {
        let ob: Promise<any> = this._callService(RESTEnum.DELETE, this.instance.delete.serviceUrl, this.instance.delete.httpHeaders, paras);
        ob.then(x => sucCallback(x)).catch(y => failCallback(y));
        //simple callback method, maybe we can provide Observable obj for complicate situation.
        //ob.toPromise().then(sucCallback(this._extractData)).catch(failCallback(this._catchHandler));
    }
    // end ===== general functions of RESTFul & Websocket =====  

    // start ===== special functions of Websocket =====
    invokeProxy(paras: any, sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/RESTfulProvider.invokeProxy", "RESTful provider no need to implement this function.", null));
        failCallback("RESTful provider no need to implement this function.");
    }

    stopRequest(requestId: number, sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/RESTfulProvider.stopRequest", "RESTful provider no need to implement this function.", null));
        failCallback("RESTful provider no need to implement this function.");
    }

    stopAllRequests(sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/RESTfulProvider.stopAllRequests", "RESTful provider no need to implement this function.", null));
        failCallback("RESTful provider no need to implement this function.");
    }

    closeConnection(sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/RESTfulProvider.closeConnection", "RESTful provider no need to implement this function.", null));
        failCallback("RESTful provider no need to implement this function.");
    }

    openConnection(token: string, sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/RESTfulProvider.openConnection", "RESTful provider no need to implement this function.", null));
        failCallback("RESTful provider no need to implement this function.");
    }
    // end ===== special functions of Websocket =====

    private _callService(method: RESTEnum, serviceUrl: string, headers?: any, paras?: any, body?: any): Promise<any> {
        let p = paras || '';
        let options: RequestOptionsArgs = new RequestOptions(
            {
                headers: new Headers(headers)
            });
        let paraRe = this._extractQueryString(p);
        switch (method) {
            case RESTEnum.GET:
                return this.http.get(serviceUrl + paraRe, options).toPromise();//.map(this._extractData).catch(this._catchHandler);
            case RESTEnum.POST:
                return this.http.post(serviceUrl + paraRe, body, options).toPromise(); //.map(this._extractData).catch(this._catchHandler);
            case RESTEnum.PUT:
                return this.http.put(serviceUrl + paraRe, body, options).toPromise(); //.map(this._extractData).catch(this._catchHandler);
            case RESTEnum.DELETE:
                return this.http.delete(serviceUrl + paraRe, options).toPromise(); //.map(this._extractData).catch(this._catchHandler);
            default:
                return undefined;
        }
    }

    private _extractData(res: Response): any {
        let body = res.json();
        return body.data || {};
    }

    private _extractQueryString(paras: any): string {
        if (paras == undefined || paras == {}) {
            return "";
        }
        else {
            let stringBuilder: string = `?`;
            for (let key of Object.keys(paras)) {
                stringBuilder = stringBuilder.concat('&');
                stringBuilder = stringBuilder.concat(key + '=' + paras[key]);
            }
            stringBuilder = stringBuilder.replace('&', '');
            return stringBuilder;
        }
    }

    private _catchHandler(error: Response | any): any {
        let errorMsg: string;
        if (error instanceof Response) {
            const body = error.json() || '';
            const err = body.error || JSON.stringify(body);
            errorMsg = `${error.status} - ${error.statusText || ''} ${err}`;
        }
        else {
            errorMsg = error.message ? error.message : error.toString();
        }
        this.logger.error(new LoggerInfo("framework/provider/restful.provider", "HTTP call failed", errorMsg));
        return Observable.throw(errorMsg);
    }
}